#ifndef MMU_H
#define MMU_H 

#include "utility/Types.h"
#include "utility/Instruction.h"

#pragma pack(1)
/**
 * @brief Page Directory Entry
 */
typedef union PDE {
    uint32_t v;
    struct {
        uint32_t present : 1;                   // 0 (P) Present; must be 1 to map a 4-KByte page
        uint32_t write_disable : 1;             // 1 (R/W) Read/write, if 0, writes may not be allowe
        uint32_t user_mode_acc : 1;             // 2 (U/S) if 0, user-mode accesses are not allowed t
        uint32_t write_through : 1;             // 3 (PWT) Page-level write-through
        uint32_t cache_disable : 1;             // 4 (PCD) Page-level cache disable
        uint32_t accessed : 1;                  // 5 (A) Accessed
        uint32_t : 1;                           // 6 Ignored;
        uint32_t ps : 1;                        // 7 (PS)
        uint32_t : 4;                           // 11:8 Ignored
        uint32_t phy_pt_addr : 20;              // physical page table address
    };
} PDEType;

/**
 * @brief Page Table Entry
 */
typedef union PTE {
    uint32_t v;
    struct {
        uint32_t present : 1;                   // 0 (P) Present; must be 1 to map a 4-KByte page
        uint32_t write_disable : 1;             // 1 (R/W) Read/write, if 0, writes may not be allowe
        uint32_t user_mode_acc : 1;             // 2 (U/S) if 0, user-mode accesses are not allowed t
        uint32_t write_through : 1;             // 3 (PWT) Page-level write-through
        uint32_t cache_disable : 1;             // 4 (PCD) Page-level cache disable
        uint32_t accessed : 1;                  // 5 (A) Accessed;
        uint32_t dirty : 1;                     // 6 (D) Dirty
        uint32_t pat : 1;                       // 7 PAT
        uint32_t global : 1;                    // 8 (G) Global
        uint32_t : 3;                           // Ignored
        uint32_t phy_page_addr : 20;            // physical page address
    };
} PTEType;

#pragma pack()

/// @brief The high 10 bits
/// @param addr 
/// @return 
static inline uint32_t GetPDEIndex(uint32_t vaddr)
{
    return (vaddr >> 22);
}

/// @brief Get page table entry address
/// @param pde 
/// @return 
static inline uint32_t GetPhysicalPageTableAddress(PDEType* pde)
{
    return pde->phy_pt_addr<<12;
}

/// @brief Get index in Page Table
/// @param vaddr 
/// @return 
static inline int GetPTEIndex(uint32_t vaddr)
{
    return (vaddr >> 12) & 0x3ff;
}

/// @brief Get physical page address
/// @param pte 
/// @return 
static inline uint32_t GetPhysicalPageAddress(PTEType* pte)
{
    return pte->phy_page_addr<<12;
}


static inline uint32_t GetPTEPerm(PTEType* pte)
{
    return (pte->v & 0x1FF);
}

/// @brief Write pde table address of current task to cr3
/// @param physicalAddress 
static inline void SetPageDirectoryTable(uint32_t physicalAddress)
{
    WriteCr3(physicalAddress);
}


#endif